import dayjs from "dayjs";
import axios from "axios";
import { Decimal } from "decimal.js";

class Common {
  static dc = function(val) {
    return new Decimal(val);
  };

  constructor() {
    /**
     * 格式化人民币展示
     * @param {Number} num 元
     * @param {Number} decimal 小数位数
     */
    this.unitConvert = function(num, decimal = 2) {
      const moneyUnits = ["元", "万元", "亿元", "万亿"];
      const dividend = 10000;
      let curentNum = num;
      let curentUnit = moneyUnits[0];
      for (let i = 0; i < 4; i++) {
        curentUnit = moneyUnits[i];
        if (strNumSize(curentNum) < 5) {
          break;
        }
        curentNum = dc(curentNum).div(dividend);
      }
      const m = { num: 0, unit: "" };
      m.unit = curentUnit;
      if (curentUnit === "亿元" || curentUnit === "万亿") {
        m.num = curentNum.toFixed(decimal);
      } else {
        m.num = curentNum.toFixed(decimal);
      }
      return m;
    };
    /**
     * 树筛选 返回筛选完数据
     * @param {Array} treeDatas
     * @param {String} name
     * @param {String} key
     * @return {Array}
     */
    this.getFilterTrees = function({ treeDatas, name, key = "name" }) {
      return new Promise(resolve => {
        let paths = new Set();
        let stark = [];
        stark = treeDatas.filter(data => data[key].includes(name));
        while (stark.length) {
          let temp = stark.shift();
          paths.add(temp);
          if (temp.parentId) {
            const parentData = treeDatas.find(
              data => data.id === temp.parentId
            );
            const hasParentData = stark.find(data => data.id === temp.parentId);
            if (parentData && !hasParentData) {
              stark.push(parentData);
            }
          }
        }
        resolve(Array.from(paths));
      });
    };
    /**
     * 获取路由父级路由路径（包含当前路由）
     * @param {String} key 父级key
     * @param {Object} route 路由{path,params,query}
     * @param {Array} routes 总路由
     * @param {Array} datas 路径数组
     * @param {String} paramRouteMap 有参数路由Map
     * @param {Object} store
     * @return {Array}
     */
    this.getParentPaths = function({
      key = "path",
      route,
      routes,
      datas = [],
      type = "",
      paramRouteMap,
      store
    }) {
      const routeData = this.getRouteNode({
        routes: routes,
        key,
        value: route.path
      });
      // 设置有参数路由Map
      const params = route.params || {};
      const query = route.query || {};
      if (Object.keys(params).length || Object.keys(query).length) {
        store.dispatch("setParamRouteMap", {
          ...routeData,
          params,
          query
        });
      }
      ///进行paramRouteMap判断
      let args = paramRouteMap ? paramRouteMap[routeData.id] : {};
      datas.unshift({ ...routeData, type, ...args });
      if (routeData && routeData["parentId"]) {
        const parentRoute =
          this.getRouteNode({
            routes: routes,
            key: "path",
            value: routeData["parentId"]
          }) ||
          this.getRouteNode({
            routes: routes,
            key: "id",
            value: routeData["parentId"]
          });
        return this.getParentPaths({
          key,
          route: parentRoute,
          routes,
          datas,
          type: "middle",
          paramRouteMap,
          store
        });
      } else {
        return datas;
      }
    };
    /**
     * 跳转路由并设置状态（需call或applay方式调用）
     * @param {Object} route 对应路由参数
     */
    this.routeTo = function(route) {
      // 设置面包屑所需路由路径
      const page = route.page || "index";
      const paths = this.$utils.getParentPaths({
        route,
        routes: this.$store.state[page].validRoutes,
        paramRouteMap: this.$store.state[page].paramRouteMap,
        store: this.$store
      });
      this.$store.dispatch("setRoutePaths", paths);
      // 跳转路由
      const params = route.params || {};
      const query = route.query || {};
      this.$router.push({ path: route.path, params, query });
      // 设置菜单选中状态
      const flatPaths = getLeafsKeyDeep(
        this.$store.state[page].pagePermissions,
        "component"
      );
      for (let len = paths.length - 1; len >= 0; len--) {
        const pathObj = paths[len];
        if (flatPaths.includes(pathObj.path)) {
          this.$store.dispatch("setActiveMenu", {
            id: pathObj.id,
            path: pathObj.path
          });
          break;
        }
      }
    };
    /**
     * 获取指定路由节点
     * @param {Array} routes 总路由
     * @param {String} key
     * @param {any} value
     * @return {Object}
     */
    this.getRouteNode = function({ routes, key, value }) {
      let stark = [];
      stark = stark.concat(routes);
      while (stark.length) {
        let temp = stark.shift();
        if (temp.children && temp.children.length) {
          stark = temp.children.concat(stark);
        }
        if (
          key === "index" &&
          value === 0 &&
          (!temp.children || !temp.children.length)
        ) {
          return temp;
        }
        if (value && temp[key] === value) {
          return temp;
        }
      }
    };
    /**
     * 获取树级节点所有子节点key
     * @param {Object} tree
     * @param {String} keyName
     */
    const getLeafsKeyDeep = function(tree, keyName = "id") {
      let stark = [];
      let models = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp.children) {
          stark = temp.children.concat(stark);
        }
        models.push(temp[keyName]);
      }
      return models;
    };
    this.getLeafsKeyDeep = getLeafsKeyDeep;
    /**
     * 获取“拉平”数组
     * @param {Object|Array} tree
     */
    this.getTreeFlatDatas = function(tree) {
      let stark = [];
      let models = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp.children) {
          stark = temp.children.concat(stark);
        }
        models.push(temp);
      }
      return models;
    };
    /**
     * 设置树级节点所有子节点key
     * @param {Object} tree
     * @param {String} keyName
     */
    this.setLeafsKeyDeep = function({ tree, key, value }) {
      let stark = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        temp[key] = value;
        if (temp.children) {
          stark = temp.children.concat(stark);
        }
      }
    };
    /**
     * 获取树指定key节点
     * @param {object} tree
     * @param {string|number} value
     * @param {string} childKey
     * @param {string} key
     * @return {object} key所在节点
     */
    this.getTreeNode = function({
      tree,
      childKey = "children",
      key = "id",
      value
    }) {
      let stark = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp[childKey] && temp[childKey].length) {
          stark = temp[childKey].concat(stark);
        }
        if (String(value) === temp[key]) {
          return temp;
        }
      }
    };
    // 获取树级节点所有子节点id
    this.getLeafsIdDeep = function(tree, keyName = "id", chooseable, nameId) {
      let stark = [];
      let models = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp.children) {
          stark = temp.children.concat(stark);
        }
        if (chooseable) {
          let checked = false;
          if (temp[chooseable] === nameId) {
            checked = true;
          }
          models.push({ id: temp[keyName], checked: checked });
        } else {
          models.push(temp[keyName]);
        }
      }
      return models;
    };
    // 遍历父节点及禁用项
    this.getLeafsIdDeepcantselect = function(
      tree,
      keyName = "id",
      judgeKeyNames
    ) {
      let stark = [];
      let models = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp.children) {
          stark = temp.children.concat(stark);
          models.push(temp[keyName]);
        }
        if (
          temp[judgeKeyNames[0]] ||
          temp[judgeKeyNames[1]] ||
          temp[judgeKeyNames[2]]
        ) {
          models.push(temp[keyName]);
        }
      }
      return models;
    };
    // checkbox @change
    this.handleCheckboxChange = function(
      id,
      checked,
      tableData,
      selectionModel,
      indeterminateModel,
      parrentKey,
      self,
      childrenName = "children",
      keyName = "id"
    ) {
      const treeNode = getTreeNode(tableData, id, childrenName, keyName);
      self.$set(indeterminateModel, id, false);
      setParentsCheckedState(
        id,
        treeNode[parrentKey],
        checked,
        tableData,
        selectionModel,
        indeterminateModel,
        parrentKey,
        self,
        childrenName,
        keyName
      );
      setChildrensCheckedState(
        treeNode,
        checked,
        selectionModel,
        indeterminateModel,
        self,
        keyName
      );
    };
    /** 获取树指定key节点
     * params {object} tree
     * params {string|number} key
     * params {string} childrenName
     * params {string} keyName
     * return {object} key所在节点
     */
    const getTreeNode = function(
      tree,
      key,
      childrenName = "children",
      keyName = "id"
    ) {
      let stark = [];
      stark = stark.concat(tree);
      while (stark.length) {
        let temp = stark.shift();
        if (temp[childrenName]) {
          stark = temp[childrenName].concat(stark);
        }
        if (String(key) === temp[keyName]) {
          return temp;
        }
      }
    };
    // 设置父级check状态
    const setParentsCheckedState = function(
      id,
      parentId,
      checked,
      tableData,
      selectionModel,
      indeterminateModel,
      parrentKey,
      self,
      childrenName = "children",
      keyName = "id"
    ) {
      const parentNode = getTreeNode(
        tableData,
        parentId,
        childrenName,
        keyName
      );
      if (!parentNode) {
        return;
      }
      let hasDiff = false;
      for (let index = 0; index < parentNode.children.length; index++) {
        const nodeId = parentNode.children[index][keyName];
        if (indeterminateModel[nodeId]) {
          hasDiff = true;
        }
        if (nodeId !== id && selectionModel[nodeId] !== checked) {
          hasDiff = true;
          break;
        }
      }
      self.$set(indeterminateModel, parentId, hasDiff);
      if (!hasDiff) {
        self.$set(selectionModel, parentId, checked);
      }
      if (parentNode.parentId !== "0") {
        setParentsCheckedState(
          parentId,
          parentNode[parrentKey],
          checked,
          tableData,
          selectionModel,
          indeterminateModel,
          parrentKey,
          self,
          childrenName,
          keyName
        );
      }
      //
    };
    // 设置子级check状态
    const setChildrensCheckedState = function(
      treeNode,
      checked,
      selectionModel,
      indeterminateModel,
      self,
      keyName = "id"
    ) {
      const childrensId = this.getLeafsIdDeep(treeNode, keyName);
      childrensId.forEach(id => {
        if (selectionModel[id] !== checked) {
          self.$set(selectionModel, id, checked);
        }
        self.$set(indeterminateModel, id, false);
      });
    }.bind(this);
    /**
     * 数组新增并返回数组（去重）
     * @param {Array} array
     * @param {any} value
     * @return {Array}
     */
    this.arrayAdd = function(array, value) {
      return Array.from(new Set([...array, value]));
    };
    /**
     * 数组删除某项并返回数组
     * @param {Array} array
     * @param {any} value
     * @return {Array}
     */
    this.arrayDelete = function(array, value) {
      const exitIndex = array.indexOf(value);
      if (~exitIndex) {
        array.splice(exitIndex, 1);
      }
      return array;
    };
    /**
     * 获取格式化文件大小,输出成带单位的字符串
     * @param {Number} size 文件大小（单位B）
     * @param {Number} [pointLength=2] 精确到的小数点数。
     * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。
     * 如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
     * @return {String}
     */
    this.formatSize = function(size, pointLength, units) {
      var unit;
      units = units || ["B", "K", "M", "G", "TB"];
      while ((unit = units.shift()) && size > 1024) {
        size = size / 1024;
      }
      return (
        (unit === "B"
          ? size
          : size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit
      );
    };
    /**
     * 获取字符串字符数
     * @param {String} str
     * @return {Number}
     */
    this.getStringCharNumber = function(str) {
      let realLength = 0;
      let charCode = -1;
      for (let i = 0, len = str.length; i < len; i++) {
        charCode = str.charCodeAt(i);
        if (charCode >= 0 && charCode <= 128) {
          realLength += 1;
        } else {
          realLength += 2;
        }
      }
      return realLength;
    };
    /**
     * 获取单行html字符串宽度（px）
     * @param {String} text
     * @param {Number} fontSize
     * @return {Number}
     */
    this.getTextWidth = function(text, fontSize) {
      const reg = /<\/?.+?\/?>/g;
      const str = text.replace(/[\r\n\s]/g, "").replace(reg, "");
      return (this.getStringCharNumber(str) * fontSize) / 2;
    };
    /**
     * 获取格式化时间
     * @param {String} time
     * @param {String} format
     * @return {String}
     */
    this.getFormatTime = function(time, format) {
      if (dayjs(time).isValid()) {
        const dayjsFormat = format.replace(/y/g, "Y").replace(/d/g, "D");
        return dayjs(time).format(dayjsFormat);
      } else {
        return "";
      }
    };
    /**
     * 设置DOM元素高度 （根据refName查找DOM）
     * @param {String} refName
     * @param {Object} context
     */
    this.getCalcHeight = function(refName, context) {
      if (context.$refs[refName]) {
        const height = `calc(100% - ${context.$refs[refName].offsetHeight}px)`;
        context.$refs[refName].nextSibling.style.height = height;
      }
    };
    /**
     * 绑定window resize事件
     * @param {String} refName
     * @param {Object} context
     */
    this.setResizeTableHeight = function(refName, context) {
      const boundFunction = getCalcHeight.bind(context, refName, context);
      window.addEventListener("resize", boundFunction);
      context.$once("hook:beforeDestroy", () => {
        window.removeEventListener("resize", boundFunction);
      });
      boundFunction();
    };
    // 禁止浏览器缩放
    this.disableWindowScale = function() {
      window.addEventListener(
        "mousewheel",
        function(event) {
          if (event.ctrlKey === true || event.metaKey) {
            event.preventDefault();
          }
        },
        { passive: false }
      );
      // firefox兼容
      window.addEventListener(
        "DOMMouseScroll",
        function(event) {
          if (event.ctrlKey === true || event.metaKey) {
            event.preventDefault();
          }
        },
        { passive: false }
      );
    };
    /**
     * 获取附件列表
     * @param {Array} fileList
     * @param {Array} cacheFileList
     * @return {Array}
     */
    this.getFormatFileList = function(fileList, cacheFileList) {
      return fileList.map(item => {
        if (item.response) {
          let cacheFile;
          if (cacheFileList) {
            cacheFile = cacheFileList.find(
              fileInfo => fileInfo.id === item.response.id
            );
          }
          if (cacheFile) {
            return cacheFile;
          } else {
            return Object.assign({}, item.response.fileInfo, {
              name: item.response.fileInfo.fileName,
              listFlags: []
            });
          }
        } else {
          item.listFlags = item.listFlags || [];
          return item;
        }
      });
    };
    /**
     * 获取数据类型
     * @param {any} value
     * @return 数据类型 如：Number|String|Array|Object|Undefined|Null|Boolean|Function|Symbol
     */
    this.getType = function(value) {
      return Object.prototype.toString.call(value).slice(8, -1);
    };
    /**
     * 每页显示条数改变 （需call或applay方式调用；固定字段pageSize、currentPage）
     * @param {Number} pageSize
     * @param {Function} callback 回调函数
     */
    this.sizeChange = function(value, callback, tableRef) {
      this.pageSize = value;
      this.currentPage = 1;
      tableRef && (this.$refs[tableRef].bodyWrapper.scrollTop = 0);
      callback && callback();
    };
    /**
     * 翻页函数 （需call或applay方式调用；固定字段currentPage）
     * @param {Number} currentPage
     * @param {Function} callback 回调函数
     */
    this.currentChange = function(value, callback, tableRef) {
      this.currentPage = value;
      tableRef && (this.$refs[tableRef].bodyWrapper.scrollTop = 0);
      callback && callback();
    };
    /**
     * 限制小数位数 （需call或applay方式调用；待form组件完善后移除）
     * @param {String} name
     * @param {String} value
     * @param {Number} decimal 限制小数位数
     */
    this.limitDecimal = function(name, value, decimal = 0) {
      this[name] = value
        .replace(/[^\d.]/g, "")
        .replace(new RegExp("\\.{" + decimal + ",}", "g"), ".")
        .replace(".", "$#$")
        .replace(/\./g, "")
        .replace("$#$", ".")
        .replace(
          new RegExp("^(-)*(\\d+)\\.([\\d]{" + decimal + "}).*$"),
          "$1$2.$3"
        )
        .replace(/^\./g, "");
    };
    /**
     * 转换字符串为数字 （需call或applay方式调用；待form组件完善后移除）
     * @param {String} name
     * @param {String} value
     */
    this.stringToNumber = function(name, value) {
      this[name] = isNaN(value) ? 0 : +value;
    };
    /**
     * 格式化时间 （el-table-colume formatter 需call或applay方式调用）
     * @param {Object} row
     * @param {Object} column
     * @param {any} cellValue
     * @param {Number} index
     * @return {String}
     */
    this.formatDateTime = function(row, column, cellValue, index) {
      return dayjs(cellValue).format("YYYY-MM-DD HH:mm:ss");
    };
    /**
     * 获取级联选择器指定id节点标签名
     * @param {string} id 要寻找的唯一值
     * @param {object} data 列表总数据
     * @param {string} lab  选项标签
     * @param {string} val 选项值
     * @param {string} list 子项字段
     * @return {string} key所在节点标签
     */
    this.echoCascaderData = function(
      id,
      data,
      lab = "label",
      val = "value",
      list = "children"
    ) {
      let value = "";
      function setData(data) {
        for (const item of data) {
          if (item[val] === id) {
            value = item[lab];
            return value;
          } else if (item[list] && item[list].length) {
            setData(item[list]);
          }
        }
      }
      setData(data);
      return value;
    };
    /**
     * 防抖函数，函数连续调用时，空闲时间必须大于或等于 wait，fn 才会执行
     * @param  {Function} fn        回调函数
     * @param  {Number}   wait      间隔
     * @param  {Boolean}  immediate 首次是否执行
     * @return {Function}           返回调用函数
     */
    this.debounce = function(fn, wait = 1000, immediate = true) {
      let timer;
      return function(...args) {
        if (timer) clearTimeout(timer);
        if (immediate) {
          let callNow = !timer;
          timer = setTimeout(() => {
            timer = null;
          }, wait);
          if (callNow) fn.apply(this, args);
        } else {
          timer = setTimeout(() => {
            fn.apply(this, args);
          }, wait);
        }
      };
    };
    /**
     * 节流函数，函数连续调用时，fn 执行频率限定为 次/delay
     * @param  {Function}   fn          回调函数
     * @param  {Number}     delay       间隔
     * @param  {Boolean}    immediate   首次是否执行
     * @return {Function}               返回调用函数
     */
    this.throttle = function(fn, delay = 1000, immediate = true) {
      let timer = null;
      let first = immediate;
      return function(...args) {
        if (first) {
          fn.apply(this, args);
          first = false;
          return;
        }
        if (timer) return;
        timer = setTimeout(() => {
          clearTimeout(timer);
          timer = null;
          fn.apply(this, args);
        }, delay);
      };
    };
    /**
     * blob转换为base64
     * @param {*} blob
     */
    this.blobToBase64 = function(blob) {
      return new Promise((resolve, reject) => {
        const fileReader = new FileReader();
        fileReader.onload = e => {
          resolve(e.target.result);
        };
        // readAsDataURL
        fileReader.readAsDataURL(blob);
        fileReader.onerror = () => {
          reject(new Error("blobToBase64 error"));
        };
      });
    };
    /**
     * 数字转换为人民币字符串
     * @param {Number} number 数字
     * @param {Number} n      小数位数
     */
    this.parseFormatNum = function(number, n) {
      if (n != 0) {
        n = n > 0 && n <= 20 ? n : 2;
      }
      number =
        parseFloat((number + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
      let sub_xs = number.split(".")[1]; //小数点后
      let show_html = "";
      if (number > 0) {
        let sub_val = number
          .split(".")[0]
          .split("")
          .reverse(); //小数点前
        for (let i = 0; i < sub_val.length; i++) {
          show_html +=
            sub_val[i] +
            ((i + 1) % 3 == 0 && i + 1 != sub_val.length ? "," : "");
        }
        if (n == 0) {
          return `￥${show_html
            .split("")
            .reverse()
            .join("")}`;
        } else {
          return `￥${show_html
            .split("")
            .reverse()
            .join("")}.${sub_xs}`;
        }
      } else {
        number = number.replace("-", "");
        let sub_val = number
          .split(".")[0]
          .split("")
          .reverse(); //小数点前
        for (i = 0; i < sub_val.length; i++) {
          show_html +=
            sub_val[i] +
            ((i + 1) % 3 == 0 && i + 1 != sub_val.length ? "," : "");
        }
        if (n == 0) {
          return `￥-${show_html
            .split("")
            .reverse()
            .join("")}`;
        } else {
          return `￥-${show_html
            .split("")
            .reverse()
            .join("")}.${sub_xs}`;
        }
      }
    };
    /**
     * 预警判断显示不同颜色
     * @param {*} key 类型
     * @returns
     */
    this.getStateColor = function(key = "WarnType") {
      const stateMap = {
        0: "#E6A23C",
        1: "#F56C6C",
        undefined: "",
        null: ""
      };
      return stateMap[this[key]];
    };
    this.getStateValue = function(row, cellValue, key = "WarnType") {
      const stateMap = {
        0: "#E6A23C",
        1: "#F56C6C",
        undefined: "#606266",
        null: "#606266"
      };
      return `<span style="color:${stateMap[row[key]]}">${cellValue}</span>`;
    };
    /**
     * 转换parentId为path（临时添加）
     * @param {*} datas
     * @param {*} totalDatas
     */
    this.transParentId = function(datas, totalDatas) {
      datas.forEach(item => {
        if (item.parentId) {
          const node = this.getTreeNode({
            tree: totalDatas,
            value: item.parentId
          });
          item.parentId = node.component;
        }
        if (item.children && item.children.length) {
          this.transParentId(item.children, totalDatas);
        }
      });
    };
    // 附件tag的新增
    this.fileTagAdd = function(tagArray, newTag) {
      return Array.from(new Set([...tagArray, newTag]));
    };
    this.fileTagDelete = function(tagArray, tagName) {
      for (let i = 0; i < tagArray.length; i++) {
        if (tagArray[i] === tagName) {
          tagArray.splice(i, 1);
        }
      }
      return tagArray;
    };
    //传入参数：需要遍历的对象，需要匹配的id，获取所有的父级节点的Id
    this.findIdList = function(data2, id, children = "children") {
      var arrRes = [];
      let obj = {
        id: 0,
        [children]: data2
      };
      let rev = (data, id) => {
        if (!data || !data[children] || !data[children].length) {
          return;
        }
        for (var i = 0; i < data[children].length; i++) {
          let item = data[children][i];
          if (item.id == id) {
            //将匹配到的结果保存到数组
            arrRes.unshift(item.id);
            //递归它的父级
            rev(obj, data.id);
            break;
          } else if (item[children] && item[children].length > 0) {
            //如果有子集，则把子集作为参数重新执行本方法
            rev(item, id);
          }
        }
      };
      rev(obj, id);
      return arrRes;
    };
  }
}
export default new Common();
